package service

import (
	"errors"
	jwtTime "github.com/dgrijalva/jwt-go/v4"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/sirupsen/logrus"
	"golang.org/x/crypto/bcrypt"
	"net/http"
	"smart-sensor-cloud/internal/model"
	"smart-sensor-cloud/pkg/auth"
	"smart-sensor-cloud/pkg/middleware"
	"smart-sensor-cloud/pkg/util"
	"strings"
	"time"
)

type (
	LoginRequest struct {
		Username string `json:"username"`
		Password string `json:"password"`
	}
	LoginResponse struct {
		Token string      `json:"token"`
		User  *model.User `json:"user"`
	}
)

// Login return User model and JWT of given username and password.
// @summary 用户登录
// @description 使用指定的用户和密码进行登录并获得JSON Web Token
// @accept json
// @produce json
// @param request body LoginRequest true "登录请求信息"
// @success 200 {object} service.Response{payload=service.LoginResponse} "登录成功响应, 包含JWT和用户信息"
// @failure 500 {object} util.Error "失败响应, 包含错误码和错误描述"
// @router /user/login [post]
func Login(ctx *gin.Context) {
	req := new(LoginRequest)
	if err := ctx.MustBindWith(req, binding.JSON); err != nil {
		ctx.Set("error", util.NewInvalidParameterError(err.Error()))
		return
	}

	if user, err := model.GetUserByUsername(req.Username); err != nil {
		if errors.Is(err, model.ErrUserNotFound) {
			ctx.Set("error", util.NewResourceNotFoundError(err.Error()))
			return
		} else {
			ctx.Set("error", &util.Error{
				Code:    http.StatusInternalServerError,
				Message: err.Error(),
			})
			return
		}
	} else {
		if err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password)); err != nil {
			if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
				ctx.Set("error", util.NewUnauthorizedRequestError("密码校验失败"))
				return
			} else {
				ctx.Set("error", &util.Error{
					Code:    http.StatusInternalServerError,
					Message: err.Error(),
				})
				return
			}
		}

		role, err := model.GetRoleWithUserIDWithoutRoleMenuRelations(int(user.ID))
		if err != nil {
			if errors.Is(err, model.ErrRoleNotFound) || errors.Is(err, model.ErrUserRoleNotFound) {
				ctx.Set("error", util.NewResourceNotFoundError(err.Error()))
			} else {
				ctx.Set("error", &util.Error{
					Code:    http.StatusInternalServerError,
					Message: err.Error(),
				})
			}
			return
		}

		GenerateJWTAndRespond(ctx, user, role)
	}
}

func GenerateJWTAndRespond(ctx *gin.Context, user *model.User, role *model.Role) {
	jwtGenerator := ctx.MustGet(middleware.JWTGeneratorKey).(*auth.JWTGenerator)

	claims := &auth.UserClaims{
		UserID:   int(user.ID),
		RoleID:   int(role.ID),
		Username: user.Username,
		StandardClaims: jwtTime.StandardClaims{
			NotBefore: jwtTime.Now(),                                // 签名生效时间，即刻生效
			ExpiresAt: jwtTime.At(time.Now().Add(15 * time.Minute)), // 签名过期时间，15分钟后
			Issuer:    auth.DefaultJWTIssuer,
		},
	}

	token, err := jwtGenerator.CreateJWT(claims)
	if err != nil {
		ctx.Set("error", &util.Error{
			Code:    http.StatusInternalServerError,
			Message: err.Error(),
		})
		return
	}

	response := &LoginResponse{
		Token: token,
		User:  user,
	}

	util.ResponseOK(ctx, util.OKWithPayload(response))
}

func Logout(ctx *gin.Context) {
	userID, exist := ctx.Get(middleware.UserIDKey)
	jwtGenerator := ctx.MustGet(middleware.JWTGeneratorKey).(*auth.JWTGenerator)

	if exist {
		if err := jwtGenerator.DestroyToken(GetToken(ctx)); err != nil {
			logrus.Errorf("用户%d登出系统失败, %s", userID, err)
		}
	}

	util.ResponseOK(ctx, util.OK())
}

func RefreshToken(ctx *gin.Context) {
	jwtGenerator := ctx.MustGet(middleware.JWTGeneratorKey).(*auth.JWTGenerator)

	if latestToken, err := jwtGenerator.RenewToken(GetToken(ctx)); err != nil {
		ctx.Set("erroor", util.ErrRefreshTokenFailed)
		return
	} else {
		util.ResponseOK(ctx, util.OKWithPayload(&LoginResponse{
			Token: latestToken,
		}))
	}
}

func GetToken(ctx *gin.Context) string {
	var token string
	authHeader := ctx.GetHeader("Authorization")
	prefix := "Bearer "
	if authHeader != "" && strings.HasPrefix(authHeader, prefix) {
		token = authHeader[len(prefix):]
	}
	return token
}
